package xjutils

import (
	"github.com/gin-gonic/gin"
	"log"
	"reflect"
	"strconv"
	"time"
)

func QueryMap(ctx *gin.Context) (m map[string]interface{}) {
	m = make(map[string]interface{})
	values := ctx.Request.URL.Query()
	for k, v := range values {
		if len(v) == 1 {
			m[k] = v[0]
		} else {
			m[k] = v
		}
	}
	return
}

func QueryInt(ctx *gin.Context, key string) (v int) {
	str := ctx.Query(key)
	v, _ = strconv.Atoi(str)
	return
}
func QueryIntDefault(ctx *gin.Context, key string, d int) (v int) {
	str := ctx.Query(key)
	if str == "" {
		return d
	}
	v, err := strconv.Atoi(str)
	if err != nil {
		return d
	}
	return
}
func QueryInt64(ctx *gin.Context, key string) (v int64) {
	str := ctx.Query(key)
	v, _ = strconv.ParseInt(str, 10, 64)
	return
}

func QueryFloat64(ctx *gin.Context, key string) (v float64) {
	str := ctx.Query(key)
	v, _ = strconv.ParseFloat(str, 64)
	return
}

func PathVariableInt(ctx *gin.Context, key string) (v int) {
	str := ctx.Param(key)
	v, _ = strconv.Atoi(str)
	return
}

func PathVariableInt64(ctx *gin.Context, key string) (v int64) {
	str := ctx.Param(key)
	v, _ = strconv.ParseInt(str, 10, 64)
	return
}

func PathVariableFloat64(ctx *gin.Context, key string) (v float64) {
	str := ctx.Param(key)
	v, _ = strconv.ParseFloat(str, 64)
	return
}

func PathVariableFloat32(ctx *gin.Context, key string) (v float32) {
	str := ctx.Param(key)
	v1, _ := strconv.ParseFloat(str, 32)
	return float32(v1)
}

func PathVariableString(ctx *gin.Context, key string) (v string) {
	str := ctx.Param(key)
	return str
}

func RequestBody(ctx *gin.Context, dst interface{}) {
	params := make(map[string]interface{})
	err := ctx.Bind(&params)
	if err != nil {
		log.Println(err)
	}
	MapToEntityWithOutType(params, dst)
}

/**
 * Map转实体类
 * @param map 需要初始化的数据，key字段必须与实体类的成员名字一样，否则赋值为空
 * @param entity  需要转化成的实体类
 * @return
 */
func MapToEntityWithOutType(src map[string]interface{}, dst interface{}) {
	dval := reflect.ValueOf(dst).Elem()
	for k, v := range src {
		key := Capitalize(k)
		//println(key)
		dvalue := dval.FieldByName(key)
		if !dvalue.IsValid() {
			//log.Printf("%s is not found. \n", key)
			continue
		}
		valueStr := ""
		srcType := ""
		switch v.(type) {
		case string:
			srcType = "string"
			valueStr = v.(string)
		case int64:
			srcType = "int64"
			valueStr = strconv.FormatInt(v.(int64), 10)
		case float64:
			srcType = "float64"
			valueStr = strconv.FormatFloat(v.(float64), 'f', -1, 64)
		case []interface{}:
			srcType = "[]interface{}"
		default:
			srcType = "other"
			log.Printf("not do,field is %s,src type is %s.\n", key, srcType)
		}
		tgtType := dvalue.Type().Name()
		if srcType == tgtType {
			//fmt.Printf("same,field is %s,src type is %s,tgt type is %s.\n",key,srcType,tgtType)
			vv := reflect.ValueOf(v)
			dvalue.Set(vv)
		} else {
			//fmt.Printf("diff,field is %s,src type is %s,tgt type is %s.\n",key,srcType,tgtType)
			switch tgtType {
			case "string":
				vv := reflect.ValueOf(valueStr)
				dvalue.Set(vv)
			case "int":
				v, _ := strconv.Atoi(valueStr)
				vv := reflect.ValueOf(v)
				dvalue.Set(vv)
			case "int8":
				v, _ := strconv.Atoi(valueStr)
				v2 := int8(v)
				vv := reflect.ValueOf(v2)
				dvalue.Set(vv)
			case "int64":
				v, _ := strconv.ParseInt(valueStr, 10, 64)
				vv := reflect.ValueOf(v)
				dvalue.Set(vv)
			case "float64":
				v, _ := strconv.ParseFloat(valueStr, 10)
				vv := reflect.ValueOf(v)
				dvalue.Set(vv)
			case "Time":
				if len(valueStr) == 10 {
					v, _ := time.ParseInLocation("2006-01-02", valueStr, time.Local)
					vv := reflect.ValueOf(v)
					dvalue.Set(vv)
				} else if len(valueStr) == 19 {
					v, _ := time.ParseInLocation("2006-01-02 15:04:05", valueStr, time.Local)
					vv := reflect.ValueOf(v)
					dvalue.Set(vv)
				} else {
					//v, _ := time.ParseInLocation("2006-01-02", "0000-01-01", time.Local)
					//vv := reflect.ValueOf(v)
					//dvalue.Set(vv)
				}
			default:
				log.Printf("error:field is %s,src type is %s,tgt type is %s.\n", key, srcType, tgtType)
			}
		}
	}
}

// Capitalize 字符首字母大写
func Capitalize(str string) string {
	var upperStr string
	vv := []rune(str) // 后文有介绍
	for i := 0; i < len(vv); i++ {
		if i == 0 {
			if vv[i] >= 97 && vv[i] <= 122 { // 后文有介绍
				vv[i] -= 32 // string的码表相差32位
				upperStr += string(vv[i])
			} else {
				log.Println("Not begins with lowercase letter,")
				return str
			}
		} else {
			upperStr += string(vv[i])
		}
	}
	return upperStr
}

////Map转实体类(包含子struct)
func MapToEntityWithOutTypeDeep(src map[string]interface{}, dst interface{}) interface{} {
	dval := reflect.ValueOf(dst).Elem()
	for k, v := range src {
		key := Capitalize(k)
		//println(key)
		dvalue := dval.FieldByName(key)
		if !dvalue.IsValid() {
			//log.Printf("%s is not found. \n", key)
			continue
		}
		valueStr := ""
		srcType := ""
		switch v.(type) {
		case string:
			srcType = "string"
			valueStr = v.(string)
		case int64:
			srcType = "int64"
			valueStr = strconv.FormatInt(v.(int64), 10)
		case float64:
			srcType = "float64"
			valueStr = strconv.FormatFloat(v.(float64), 'f', -1, 64)
		case []interface{}:
			srcType = "[]interface{}"
			if dvalue.Type().Kind() == reflect.Slice {
				SliceReflect := reflect.MakeSlice(dvalue.Type(), 0, 0)
				for _, item := range v.([]interface{}) {
					typ := dvalue.Type().Elem()
					ditem := reflect.New(typ)
					dst := MapToEntityWithOutTypeDeep(item.(map[string]interface{}), ditem.Interface())
					SliceReflect = reflect.Append(SliceReflect, reflect.ValueOf(dst).Elem())
				}
				dvalue.Set(SliceReflect)
				continue
			}
		case map[string]interface{}:
			srcType = "map[string]interface{}"
			if dvalue.Type().Kind() == reflect.Struct {
				typ := dvalue.Type()
				ditem := reflect.New(typ)
				MapToEntityWithOutTypeDeep(v.(map[string]interface{}), ditem.Interface())
				dvalue.Set(ditem.Elem())
				continue
			}
		default:
			srcType = "other"
			log.Printf("not do,field is %s,src type is %s.\n", key, srcType)
		}
		tgtType := dvalue.Type().Name()
		ptr := false
		if dvalue.Type().Kind().String() == "ptr" {
			elem := dvalue.Type().Elem()
			tgtType = elem.Name()
			ptr = true
		}
		if srcType == tgtType {
			//fmt.Printf("same,field is %s,src type is %s,tgt type is %s.\n",key,srcType,tgtType)
			vv := reflect.ValueOf(v)
			dvalue.Set(vv)
		} else {
			//fmt.Printf("diff,field is %s,src type is %s,tgt type is %s.\n",key,srcType,tgtType)
			switch tgtType {
			case "string":
				vv := reflect.ValueOf(valueStr)
				dvalue.Set(vv)
			case "int":
				v, _ := strconv.Atoi(valueStr)
				vv := reflect.ValueOf(v)
				dvalue.Set(vv)
			case "int8":
				v, _ := strconv.Atoi(valueStr)
				v2 := int8(v)
				vv := reflect.ValueOf(v2)
				dvalue.Set(vv)
			case "int64":
				v, _ := strconv.ParseInt(valueStr, 10, 64)
				vv := reflect.ValueOf(v)
				dvalue.Set(vv)
			case "float64":
				v, _ := strconv.ParseFloat(valueStr, 10)
				vv := reflect.ValueOf(v)
				dvalue.Set(vv)
			case "Time":
				if len(valueStr) == 10 {
					v, _ := time.ParseInLocation("2006-01-02", valueStr, time.Local)
					if ptr {
						vv := reflect.ValueOf(&v)
						dvalue.Set(vv)
					} else {
						vv := reflect.ValueOf(v)
						dvalue.Set(vv)
					}
				} else if len(valueStr) == 19 {
					v, _ := time.ParseInLocation("2006-01-02 15:04:05", valueStr, time.Local)
					if ptr {
						vv := reflect.ValueOf(&v)
						dvalue.Set(vv)
					} else {
						vv := reflect.ValueOf(v)
						dvalue.Set(vv)
					}
				}
			case "XjTime":
				if len(valueStr) == 10 {
					v1, _ := time.ParseInLocation("2006-01-02", valueStr, time.Local)
					v := XjTime(v1)
					if ptr {
						vv := reflect.ValueOf(&v)
						dvalue.Set(vv)
					} else {
						vv := reflect.ValueOf(v)
						dvalue.Set(vv)
					}
				} else if len(valueStr) == 19 {
					v1, _ := time.ParseInLocation("2006-01-02 15:04:05", valueStr, time.Local)
					v := XjTime(v1)
					if ptr {
						vv := reflect.ValueOf(&v)
						dvalue.Set(vv)
					} else {
						vv := reflect.ValueOf(v)
						dvalue.Set(vv)
					}
				}
			case "XjDate":
				if len(valueStr) >= 10 {
					v1, _ := time.ParseInLocation("2006-01-02", valueStr, time.Local)
					v := XjDate(v1)
					if ptr {
						vv := reflect.ValueOf(&v)
						dvalue.Set(vv)
					} else {
						vv := reflect.ValueOf(v)
						dvalue.Set(vv)
					}
				}
			default:
				log.Printf("error:field is %s,src type is %s,tgt type is %s.\n", key, srcType, tgtType)
			}
		}
	}
	return dst
}
